
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Diagnostics;
using System.Xml;

using Swaf;
using Swaf.Container;
using Swaf.Security;

namespace Swaf.DataAccess
{
	/// <summary>
	/// Addess support for security for DARs by verifying that the current user
	/// has execute privileges for the provided security resource.
	/// </summary>
	/// <remarks>
	/// By adding the security="" option to the Decorated DAR catalog information
	/// this decorator will be added.  The value for the security attribute needs
	/// to be a valid reource name for the current user.  However, the resource
	/// name can be derived from values passed into the DAR call by using curly
	/// braces within the resource name.  For example, the following 
	/// resource name is fixed:
	/// 
	///		BizRules.RuleName
	/// 
	/// The following resource name is dynamic because part of its name
	/// comes from values within the parameters passed into the DAR:
	/// 
	///		BizRule.{RuleName}
	/// 
	/// The {RuleName} will be replaced by using "RuleName" as a key
	/// into the INameAddressSupport or IDictionary passed into the DAR to
	/// do the execute.  The value of the entry will be used to create
	/// the actual resource name.  For example, if the "RuleName" key
	/// was associated with the value "ExampleBizRule", the actual 
	/// resource name checked for the execute privilege would be:
	/// 
	///		Bizrule.ExampleBizRule
	///	
	/// This dynamic behavior allows the actual privilege to be
	/// derived from data.
	/// 
	/// NOTE: There can be only one set of curly braces in the name. 
	/// This implementation does not check for multiple sets of 
	/// curly braces.</remarks>
	public class DarSecurityDecorator : GenericDecorator, IDarDecorator
	{
		protected string[] m_resourceNameParts;

		/// <summary>
		/// Gets ready for the execution of the DAR by getting the resource
		/// name ready.  If the resource name contains curly braces if will
		/// preprocess the name in order to execute faster.</summary>
		/// <param name="name">The name of the decorator. Presumed to
		/// be security, but doesn't really matter as it isn't used.</param>
		/// <param name="securityInfo">The resource name to verify the
		/// execute security privilege on.</param>
		/// <param name="otherInfo">The XmlElement with the DAR catalog information.</param>
		public DarSecurityDecorator(string name, string securityInfo, object otherInfo)
			:base(name, securityInfo, otherInfo)
		{
			XmlElement requestInfo = otherInfo as XmlElement;
			if (securityInfo != null && securityInfo.Length > 0)
			{
				int paramRef = securityInfo.IndexOf('{');
				if (paramRef == -1)
					m_resourceNameParts = new string[] { securityInfo };
				else
				{
					int paramEndRef = securityInfo.IndexOf('}');
					m_resourceNameParts = new string[3];
					m_resourceNameParts[0] = securityInfo.Substring(0, paramRef);
					m_resourceNameParts[1] = securityInfo.Substring(paramRef + 1, paramEndRef - paramRef - 1);
					m_resourceNameParts[2] = securityInfo.Substring(paramEndRef + 1).TrimStart('.');
				}
			}
			else
			{
				throw new DataAccessException(
					String.Format("You must supply a resource name to be used for the security attribute in the DAR '{0}'. Value was empty",
					requestInfo == null ? "UnknownName" : requestInfo.GetAttribute("name")));
			}
		}

		#region IDarDecorator Members
		/// <summary>
		/// Does an ISecurityPrincipal.assert on the given resource name using the
		/// Privileges.Execute privilege level.  If the user does not have this
		/// security privelege the assert will thrown an exception.</summary>
		/// <param name="info">The DAR execute info passed by the client of the DAR</param>
		public void preExecute(ref INameAddressSupport info)
		{
			if (m_resourceNameParts != null)
			{
				ISecurityPrincipal principal = Application.currentApp.securityMgr.currentPrincipal;
				Debug.Assert(principal != null);

				string resourceName = null;
				if (m_resourceNameParts.Length == 1)
					resourceName = m_resourceNameParts[0];
				else
				{
					string extrVal = info.get(m_resourceNameParts[1]).ToString();
					resourceName = m_resourceNameParts[0] + extrVal + m_resourceNameParts[2];
				}
				Debug.Assert(resourceName != null);
				if (resourceName != null)
					principal.assert(resourceName, Privileges.Execute);
			}
		}

		/// <summary>
		/// Not implemented by this decorator as it isn't needed for security.</summary>
		public void postExecute(ref INameAddressSupport info, ref IDataResponse resp)
		{
			//Intentionally left blank. This decorator doesnt need to implement this method.
		}

		/// <summary>
		/// Not implemented by this decorator as it isn't needed for security.</summary>
		public void postFormat(ref object formatedValue)
		{
			//Intentionally left blank. This decorator doesnt need to implement this method.
		}
		#endregion

		#region IDecorator Members
		/// <summary>
		/// This is a standin in order to verify security.</summary>
		public override bool IsStandin { get{ return true; } }
		#endregion
	}
}
